package com.artfii.hibernate;

import org.hibernate.EmptyInterceptor;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

/**
 * Hibernate 分表拦截器（按年）
 * 保存实体时，拦截后按传入的新表名替换SQL里的旧表，来实现分表保存
 *
 * @author: lqf 2020-12-23
 */
@Component
public class HibernateShardingInterceptor extends EmptyInterceptor {
    public final static Logger logger = LoggerFactory.getLogger(HibernateShardingInterceptor.class);

    private String table;
    private String newTable;

    public HibernateShardingInterceptor() {
    }

    public HibernateShardingInterceptor(String table) {
        this.table = table;
    }

    /**
     * 创建拦截器
     *
     * @param oldTable 按数据库的表名(下划线格式）
     * @return
     */
    public static HibernateShardingInterceptor ofYearSharding(String oldTable) {
        HibernateShardingInterceptor interceptor = new HibernateShardingInterceptor(oldTable);
        interceptor.newTable = oldTable + "_" + LocalDateTime.now().getYear();
        return interceptor;
    }

    public void setNewTable(String newTable) {
        this.newTable = newTable;
    }

    /**
     * 改写原生 SQL 把表名改为新的分表
     *
     * @param sql
     * @return
     */
    @Override
    public String onPrepareStatement(String sql) {
        logger.debug(" Old sql : " + sql);
        sql = sql.replaceAll(table, newTable);
        logger.debug(" New sharding sql : " + sql);
        return super.onPrepareStatement(sql);
    }

    public void save(SessionFactory sessionFactory, Object entity) {
        saveOrupdate(sessionFactory, entity, true);
    }

    public void update(SessionFactory sessionFactory, Object entity) {
        saveOrupdate(sessionFactory, entity, false);
    }

    /**
     * 创建新的 session 并把实体保存到分表中
     *
     * @param sessionFactory
     * @param entity
     */
    private void saveOrupdate(SessionFactory sessionFactory, Object entity, boolean isSave) {
        Session session = null;
        try {
            session = buildNewSession(sessionFactory);
            if (isSave) {
                session.save(entity);
            } else {
                session.update(entity);
            }
            session.flush();
        } catch (HibernateException e) {
            logger.error("使用 HIBENATE 分表拦截器保存实体时出错，可能是分表不存在。表名：{},实体：{}", newTable, entity);
            e.printStackTrace();
        } finally {
            if (null != session) {
                session.close();
            }
        }
    }

    private Session buildNewSession(SessionFactory sessionFactory) {
        return sessionFactory.withOptions().interceptor(this).openSession();//创建新的session
    }


}
